import { Injectable } from '@nestjs/common';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import { Like, Repository, In, EntityManager } from 'typeorm';
import { instanceToPlain, plainToInstance } from 'class-transformer';
import { genSalt, hash, compare, genSaltSync, hashSync } from 'bcryptjs';

import { InfoEntity } from '../system/info/entities/info.entity';
import { BannerEntity } from '../home/entities/banner.entity';
import { HomeContentEntity } from '../home/entities/home-content.entity';
import { HomeContentListEntity } from '../home/entities/home-content-list.entity';
import { UserEntity } from '../client/clientuser/entities/clientuser.entity';
import { ColumnEntity } from '../content/entities/column.entity'
import { ContentEntity } from '../content/entities/content.entity'
import { TagsEntity } from '../content/entities/tags.entity'
import { PartnerEntity } from '../partner/entities/partner.entity'
import { MessageEntity } from '../message/entities/message.entity'
import { ContentCollectEntity } from '../content/entities/content-collect.entity'
import { AccessEntity } from '../client/access/entities/access.entity'
import { MonitoringLogEntity } from '../project-monitoring/entities/monitoring-log.entity'
import { ProjectEntity } from '../project-monitoring/entities/project.entity'

import { ContentTagsService } from '../content/content-tags.service'

import { listToTree, formerlyMonth } from '../common/utils/utils'

import { CreateClientUserDto } from '../client/clientuser/dto/create-clientuser.dto'
import { CreateMessageDto } from '../message/dto/create-message.dto'
import { UpdateContentCollectDto } from './dto/update-content-collect.dto'
import { UpdateContentConsultDto } from './dto/update-content-consult.dto'
import { CreateAccessDto } from '../client/access/dto/create-access.dto'
import { CreateMonitoringLogDto } from '../project-monitoring/dto/create-monitoring-log.dto'

import { ResultData } from '../common/utils/result';
import { dateStrFormat } from '../common/utils/utils';
import { AppHttpCode } from '../common/enums/code.enum';

import { EmailService } from 'src/common/email/email.service';
import { GetSettingService } from 'src/system/setting/get.setting.service';
import { PublicService } from 'src/public/public.service';

import config from '../config/index';
import ip2region from 'ip2region';

import { Observable, interval, map } from 'rxjs';
import { sseEvent } from 'src/common/sseEvent/utils';

@Injectable()
export class HomeService {
  private recomAllList = [
      {
        value: 'recom',
        label: '推荐'
      },
      {
        value: 'frontPage',
        label: '头条'
      }
    ]
  constructor(
    @InjectRepository(InfoEntity)
    private readonly infoRepo: Repository<InfoEntity>,
    @InjectRepository(BannerEntity)
    private readonly bannerRepo: Repository<BannerEntity>,
    @InjectRepository(HomeContentEntity)
    private readonly homeContentRepo: Repository<HomeContentEntity>,
    @InjectRepository(HomeContentListEntity)
    private readonly homeContentListRepo: Repository<HomeContentListEntity>,
    @InjectRepository(UserEntity)
    private readonly userRepo: Repository<UserEntity>,
    @InjectRepository(ColumnEntity)
    private readonly columnRepo: Repository<ColumnEntity>,
    @InjectRepository(ContentEntity)
    private readonly contentRepo: Repository<ContentEntity>,
    @InjectRepository(TagsEntity)
    private readonly tagsRepo: Repository<TagsEntity>,
    @InjectRepository(PartnerEntity)
    private readonly partnerRepo: Repository<PartnerEntity>,
    @InjectRepository(MessageEntity)
    private readonly messageRepo: Repository<MessageEntity>,
    @InjectRepository(ContentCollectEntity)
    private readonly contentCollectRepo: Repository<ContentCollectEntity>,
    @InjectRepository(AccessEntity)
    private readonly accessRepo: Repository<AccessEntity>,
    @InjectRepository(MonitoringLogEntity)
    private readonly monitoringLogRepo: Repository<MonitoringLogEntity>,
    @InjectRepository(ProjectEntity)
    private readonly projectRepo: Repository<ProjectEntity>,
    @InjectEntityManager()
    private readonly manager: EntityManager,
    private readonly contentTagsService: ContentTagsService,
    private readonly emailService: EmailService,
    private readonly getSettingService: GetSettingService,
    private readonly publicService: PublicService,
    // private readonly operalogService: OperalogService,
  ) {}
  
  /** 查询网站信息 */
  async findWebInfo() {
    let id = '1'
    let webInfo = await this.infoRepo.findOne({ where: { id } });
    webInfo.logo = `${config().app.file.domain}${webInfo.logo}`
    webInfo.qrCode = `${config().app.file.domain}${webInfo.qrCode}`
    webInfo = plainToInstance(InfoEntity, { ...webInfo }, { enableImplicitConversion: true })
    return ResultData.ok(instanceToPlain(webInfo))
  }

  /** 查询首页banner所有 */
  async findHomeBannerList() {
    const where = {
      status: 1, // 未禁用
      isDelete: 1 // 未删除
    }
    const banner = await this.bannerRepo.findAndCount({
      where,
      order: { id: 'DESC' }
    })
    const returnBan = banner[0];
    returnBan.forEach(item => {
      item.img = item.img.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)
    })

    return ResultData.ok({ list: instanceToPlain(banner[0]), total: banner[1] })
  }

  /** 查询首页内容列表信息
   * code: 首页内容标识
   */
  async findHomeContent(code: string) {
    if (!code) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '标识码错误');
    let homeContent = await this.homeContentRepo.findOne({ where: { code: code } })
    if (!homeContent) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, 'This page does not exist or has been removed');
    homeContent = plainToInstance(HomeContentEntity, { ...homeContent }, { enableImplicitConversion: true })
    
    homeContent.imgs = homeContent.imgs.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)
    let dataList = {
      dataList: []
    }

    const homeContentList = await this.homeContentListRepo.findAndCount({
      where: {
        contentCode: code
      }
    })
    if (!homeContentList) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, 'The home page content list does not exist or has been removed')
    dataList.dataList = homeContentList[0]
    dataList.dataList.forEach(item => {
      item.imgs = item.imgs.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)
    })
    let data = { ...homeContent, ...dataList }
    return ResultData.ok(instanceToPlain(data))
  }

  /** 客户端用户注册 */
  async findOneByAccount(account: string): Promise<UserEntity> {
    return await this.userRepo.findOne({ where: { account } })
  }
  async createClientUser(dto: CreateClientUserDto): Promise<ResultData> {
    if (dto.password !== dto.confirmPassword)
      return ResultData.fail(AppHttpCode.USER_PASSWORD_INVALID, 'The two passwords are inconsistent. Please try again')
    // 防止重复创建 start
    if (await this.findOneByAccount(dto.account))
      return ResultData.fail(AppHttpCode.USER_CREATE_EXISTING, 'Account already exists, please adjust and re-register!')
    // 防止重复创建 end
    const salt = await genSalt()
    dto.password = await hash(dto.password, salt)
    // plainToInstance  忽略转换 @Exclude 装饰器
    const user = plainToInstance(UserEntity, { salt, ...dto }, { ignoreDecorators: true })
    const result = await this.manager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.save<UserEntity>(user)
    })
    return ResultData.ok(instanceToPlain(result))
  }

  /**
   * 客户端用户登录
   * account：帐号
   */
  async clientUserLogin(account: string, password: string): Promise<ResultData> {
    let user = null
    user = await this.findOneByAccount(account)

    if (!user) return ResultData.fail(AppHttpCode.USER_PASSWORD_INVALID, 'The account or password is incorrect')
    
    const checkPassword = await compare(password, user.password)
    if (!checkPassword) return ResultData.fail(AppHttpCode.USER_PASSWORD_INVALID, 'The account or password is incorrect');
    
    if (user.status === 0) return ResultData.fail(AppHttpCode.USER_ACCOUNT_FORBIDDEN, 'You have been disabled. If you need to use it normally, contact the administrator')

    return ResultData.ok(instanceToPlain(user))
  }

  /** 查询栏目所有 */
  async findColumnAllList() {
    const where = {
      isDelete: 1
    }
    const queryData = await this.columnRepo.findAndCount({
      where,
      order: { orderNum: 'DESC' }
    })
    const columnTreelist = listToTree(queryData[0], { root: '0', pidKey: 'parentId' })
    return ResultData.ok(columnTreelist)
  }

  /** 根据id查询兄弟栏目 */
  async findColumnSiblings(template) {
    // 根据父级id查找所有栏目信息
    const where = {
      template,
      isDelete: 1
    }
    const queryData = await this.columnRepo.findAndCount({
      where,
      order: { orderNum: 'DESC' }
    })
    return ResultData.ok(queryData[0])
  }

  /** 根据模板查找栏目内容并以map对象返回 */
  async findColumnSiblingsContnet(template) {
    // 根据父级id查找所有栏目信息
    const where = {
      template,
      isDelete: 1
    }
    const queryData = await this.columnRepo.findAndCount({
      where,
      order: { orderNum: 'DESC' }
    })

    let contentColumnArray = queryData[0].map((item) => {
      return {
        id: item.id,
        title: item.title
      };
    })

    let columnIdArray = queryData[0].map((item) => {
      return item.id
    })

    // const contentList = await this.findContentList(columnIdArray.join(','), 1, 9);
    return ResultData.ok(contentColumnArray)
  }

  /** 查询栏目单条数据 */
  async findOneColumnById(id: string): Promise<ColumnEntity> {
    let info = await this.columnRepo.findOne({ where: { id } })
    info = plainToInstance(ColumnEntity, { ...info }, { enableImplicitConversion: true })
    info.content = info.content.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)
    // info.img = info.img.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)
    return info
  }
  
  /** 查询栏目单条信息 */
  async findColumnOne(id: string) {
    const queryData = await this.findOneColumnById(id)
    if (!queryData) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该栏目不存在或已删除')
    queryData.content = queryData.content.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)
    queryData.img = queryData.img.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)

    return ResultData.ok(queryData)
  }

  /** 根据栏目id内容分页查询 */
  async findContentList(id: string, page: number, size: number): Promise<ResultData> {
    const ids = id.split(',');

    const where = {
      columnId: In(ids),
      status: 1,
      isDelete: 1
    }
    const queryData = await this.contentRepo.findAndCount({
      where,
      order: { id: 'DESC' },
      skip: size * (page - 1),
      take: size,
    })

    // 获取推荐返回推荐文本给前端
    const returnDataList = JSON.parse(JSON.stringify(queryData[0]))
    returnDataList.forEach(item => {
      item.img = item.img.split(',')
      item.img = item.img.length !== 0 ? item.img[0].replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`) : '';
      item.publishDate = dateStrFormat(item.publishDate)
    })

    return ResultData.ok({ list: instanceToPlain(returnDataList), total: queryData[1] })
  }

  /** 根据栏目id内容分页查询 */
  async findContentSearchList(search: string, page: number, size: number) {
    const where = {
      title: Like(`%${search}%`),
      status: 1,
      isDelete: 1
    }
    const queryData = await this.contentRepo.findAndCount({
      where,
      order: { id: 'DESC' },
      skip: size * (page - 1),
      take: size,
    })

    // 获取推荐返回推荐文本给前端
    const returnDataList = JSON.parse(JSON.stringify(queryData[0]))
    returnDataList.forEach(item => {
      item.img = item.img.split(',')
      item.img = item.img.length !== 0 ? item.img[0].replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`) : '';
      item.publishDate = dateStrFormat(item.publishDate)
    })

    return ResultData.ok({ list: instanceToPlain(returnDataList), total: queryData[1] })
  }

  /** 根据栏目id查询最近3篇内容 */
  async findContentRecent(id: string) {
    const where = {
      columnId: id,
      status: 1,
      isDelete: 1
    }
    const queryData = await this.contentRepo.findAndCount({
      where,
      order: { id: 'DESC' },
      skip: 3 * (1 - 1),
      take: 3,
    })
    const returnDataList = JSON.parse(JSON.stringify(queryData[0]))

    returnDataList.forEach(item => {
      item.img = item.img.split(',')
      item.img = item.img.length !== 0 ? item.img[0].replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`) : '';
      item.publishDate = dateStrFormat(item.publishDate)
    })

    return ResultData.ok(instanceToPlain(returnDataList))
  }

  /** 查询内容单条信息 */
  async findContentOne(id: string) {
    let queryData = await this.contentRepo.findOne({ 
      where: { id }
    })
    if (!queryData) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该内容不存在或已删除');
    queryData.content = queryData.content.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)
    const commonList = await this.messageRepo.findAndCount({
      where: {
        contentId: id,
      },
      order: { id: 'DESC' }
    })
    // 查询内容对应标签
    const tagsIds = await this.contentTagsService.findContentTagsByContentId(id);
    /** 查询标签所有 */
    const where = {
      id: In(tagsIds),
      isDelete: 1
    }
    const queryTagData = await this.tagsRepo.findAndCount({
      where,
      order: { id: 'DESC' }
    })
    // 深拷贝内容数据并把标签赋值
    const returnData = JSON.parse(JSON.stringify(queryData))
    returnData.img = returnData.img.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`)
    returnData.tags = queryTagData[0]
    returnData.commonList = commonList[0]
    returnData.commonTotal = commonList[1]
    return ResultData.ok(instanceToPlain(returnData))
  }

  /** 获取推荐产品 */
  async findRecomProduct() {
    const where = {
      recom: Like('%recom%'),
      status: 1,
      isDelete: 1
    }
    const queryData = await this.contentRepo.findAndCount({
      where,
      order: { id: 'DESC' }
    })
    const returnDataList = JSON.parse(JSON.stringify(queryData[0]))

    returnDataList.forEach(item => {
      item.img = item.img.split(',')
      item.img = item.img.length !== 0 ? item.img[0].replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`) : '';
      item.publishDate = dateStrFormat(item.publishDate)
    })

    return ResultData.ok(instanceToPlain(returnDataList))
  }

  /** 查询合作伙伴分页查询 */
  async findPartnerList() {
    const where = {
      status: 1,
      isDelete: 1
    }
    const partner = await this.partnerRepo.findAndCount({
      where,
      order: { id: 'DESC' },
    })

    const returnDataList = partner[0]
    returnDataList.forEach(item => {
      item.logo = item.logo !== '' ? item.logo.replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`) : '';
    })
    return ResultData.ok({ list: instanceToPlain(returnDataList) })
  }

  /** 创建客户留言 */
  async submitMessage(createMessageDto: CreateMessageDto, ip: string) {
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    const queryIp = new ip2region();
    const ipAddress = queryIp.search(ip);

    const message = plainToInstance(MessageEntity, createMessageDto, { ignoreDecorators: true })
    message.messageIp = ip;
    message.address = `${ipAddress.province}-${ipAddress.city}`;

    /** 先插入数据库 */
    const result = await this.manager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.save<MessageEntity>(message)
    })
    if (!result) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '保存留言失败');
    let settingInfo = await this.getSettingService.findOneInfo()
    /** 再发送邮箱 */
    const content = {
      email: createMessageDto.email,
      content: `
        姓名：${createMessageDto.name} <br/>
        电话号码：${createMessageDto.phone} <br/>
        邮箱：${createMessageDto.email} <br/>
        留言ip：${ip} <br/>
        留言地区：${ipAddress.province}-${ipAddress.city} <br/>
        留言产品网址：${createMessageDto.messageUrl} <br/>
        留言产品名称：${createMessageDto.contentName} <br/>
        留言内容：${createMessageDto.content}
      `
    };
    const contentStr = JSON.stringify(content)
    const mailInfo = await this.emailService.sendEmail('来自AI营销官网的留言', contentStr, settingInfo.receiveMail);

    if (!mailInfo) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '发送邮件失败');
    const messageData = await this.publicService.findMessageList();
    sseEvent.getEvent().emit('send', messageData.data);
    return ResultData.ok();
  }

  async getContentIsCollect(contentId: string, userId: string) {
    /** 查询是否收藏 */
    const isUserCollect = await this.contentCollectRepo.findOne({ 
      where: { userId: userId, contentId: contentId }
    })
    if (isUserCollect && isUserCollect.collect === 1) return ResultData.ok({ isCollect: true })
    return ResultData.ok({ isCollect: false })
  }

  /** 更新内容收藏量 */
  async updateContentCollect(dto: UpdateContentCollectDto) {
    /** 查询是否收藏 */
    const isUserCollect = await this.contentCollectRepo.findOne({ 
      where: { userId: dto.userId, contentId: dto.contentId }
    })
    
    const collectNumData = instanceToPlain(dto)
    let queryData = await this.contentRepo.findOne({ 
      where: { id: collectNumData.contentId }
    })
    if (!queryData) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该内容不存在或已删除');
    if (isUserCollect) {
      if (dto.collect === 0) {
        // 当收藏表里有数据和传过来的收藏是0就减去和改状态
        queryData.collectNum = queryData.collectNum > 0 ? queryData.collectNum - 1 : 0;
      }
      if (dto.collect === 1) {
        // 当收藏表里有数据和传过来的收藏是1就加去和改状态
        queryData.collectNum = queryData.collectNum >= 0 ? queryData.collectNum + 1 : 0;
      }
    } else {
      // 当收藏表没有数据和传过来的收藏不是1就加和插入收藏表
      queryData.collectNum = queryData.collectNum >= 0 ? queryData.collectNum + 1 : 0;
    }
    
    const existing = plainToInstance(ContentCollectEntity, { ...dto }, { ignoreDecorators: true })
    const { affected } = await this.manager.transaction(async (transactionalEntityManager) => {
      /** 插入/更新收藏表 */
      if (isUserCollect) {
        isUserCollect.collect = 0;
        await transactionalEntityManager.update<ContentCollectEntity>(ContentCollectEntity, isUserCollect.id, dto)
      } else {
        await transactionalEntityManager.save<ContentCollectEntity>(existing) 
      }
      
      /** 插入内容表 */
      return await transactionalEntityManager.update<ContentEntity>(ContentEntity, dto.contentId, queryData)
    })
    if (!affected) return ResultData.fail(AppHttpCode.SERVICE_ERROR, '收藏失败，请稍后尝试');
    return ResultData.ok()
  }
  
  /** 更新内容咨询量 */
  async updateContentConsult(dto: UpdateContentConsultDto, ip: string) {
    const consultNumData = instanceToPlain(dto)
    let queryData = await this.contentRepo.findOne({ 
      where: { id: consultNumData.id }
    })
    if (!queryData) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该内容不存在或已删除');
    queryData.consultNum = queryData.consultNum + 1;
    /** 先更新内容咨询量 */
    const { affected } = await this.manager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.update<ContentEntity>(ContentEntity, dto.id, queryData)
    })
    if (!affected) return ResultData.fail(AppHttpCode.SERVICE_ERROR, '更新咨询量失败，请稍后尝试');
    /** 再更新内容-询盘 */
    const messData = {
      name: dto.name,
      phone: dto.phone,
      messageIp: dto.messageIp,
      address: dto.address,
      messageUrl: dto.messageUrl,
      contentName: dto.contentName,
      contentId: dto.contentId,
      email: dto.email,
      content: dto.content,
    }
    const messResult = await this.submitMessage(messData, ip);
    if (messResult.code !== 200) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '保存留言失败');
    sseEvent.getEvent().emit('send', messResult.data);
    return ResultData.ok()
  }

  /** 获取推荐/头条产品 */
  async findContentRecommList(code: string) {
    const where = {
      recom: Like(`%${code}%`),
      status: 1,
      isDelete: 1
    }
    const queryData = await this.contentRepo.findAndCount({
      where,
      order: { id: 'DESC' }
    })
    const returnDataList = JSON.parse(JSON.stringify(queryData[0]))

    returnDataList.forEach(item => {
      item.img = item.img.split(',')
      item.img = item.img.length !== 0 ? item.img[0].replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`) : '';
      item.publishDate = dateStrFormat(item.publishDate)
    })

    return ResultData.ok(instanceToPlain(returnDataList))
  }

  /** 获取推荐/头条产品 */
  async findColumnContentNew(size: number) {
    const where = {
      status: 1,
      isDelete: 1
    }
    const queryData = await this.contentRepo.findAndCount({
      where,
      order: { createDate: 'DESC' },
      skip: size * (1 - 1),
      take: size,
    })
    const returnDataList = JSON.parse(JSON.stringify(queryData[0]))

    returnDataList.forEach(item => {
      item.img = item.img.split(',')
      item.img = item.img.length !== 0 ? item.img[0].replace(new RegExp('/static/', 'g'), `${config().app.file.domain}/static/`) : '';
      item.publishDate = dateStrFormat(item.publishDate)
    })

    return ResultData.ok(instanceToPlain(returnDataList))
  }

  /** 添加访问记录*/
  async createAccess(createMessageDto: CreateAccessDto, ip: string) {
    const access = plainToInstance(AccessEntity, createMessageDto, { ignoreDecorators: true })
    const queryIp = new ip2region();
    const ipAddress = queryIp.search(ip);
    access.district = ipAddress.isp === '本机地址' ? ipAddress.isp : `${ipAddress.province}-${ipAddress.city}`;

    const result = await this.manager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.save<AccessEntity>(access)
    })
    return ResultData.ok(instanceToPlain(result));
  }

  /** 添加项目监控日志*/
  async createMonitoringLog(createMonitoringLogDto: CreateMonitoringLogDto, key: string) {
    
    // key不能为空或者错误
    if (!key) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '请传入正确的key')
    const project = await this.projectRepo.findOne({ where: { projectKey: key, status: 1 } })
    // 项目不存在或者已经被禁用或者传入错误的key
    if (!project) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, 'key是非法的，请传入正确的key')

    createMonitoringLogDto.projectId = project.id
    createMonitoringLogDto.projectName = project.projectName
    
    const monitoringLog = plainToInstance(MonitoringLogEntity, createMonitoringLogDto, { ignoreDecorators: true });

    const result = await this.manager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.save<MonitoringLogEntity>(monitoringLog)
    })

    if (!result) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '添加项目监控日志失败');
    return ResultData.ok();
  }
}
